#include "../inc/DeviceObj.hpp"
#include "Client_types.h"
#include "NodeClient.h"
#include <cstddef>
#include <cstdint>
#include <fcntl.h>
#include <format>
#include <mutex>
#include <nlohmann/json.hpp>
#include <semaphore.h>
#include <stdexcept>
#include <string>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

DeviceObj::DeviceObj(Thrift::NodeClientClient *_client, std::string _UUID)
    : client(_client), UUID(_UUID) {
  std::string ret;
  client->GetShareMemInfo(ret, _UUID);
  nlohmann::json j = nlohmann::json::parse(ret);
  std::string MemName = j["MemName"].get<std::string>();
  std::string SemName = j["SemName"].get<std::string>();
  int fd = shm_open(MemName.c_str(), O_EXCL | O_RDONLY, 0);
  if (fd == -1)
    throw std::runtime_error(std::format("Can't Open ShareMemory {}", MemName));
  sem = sem_open(SemName.c_str(), O_EXCL | O_RDWR);
  if (sem == SEM_FAILED)
    throw std::runtime_error(std::format("Can't Get Semaphore {}", SemName));

  struct stat meminfo;
  if (fstat(fd, &meminfo) == -1)
    throw std::runtime_error(
        std::format("Can't Get ShareMemory Info {}", MemName));
  memsize = meminfo.st_size;
  mem = (unsigned char *)mmap(nullptr, meminfo.st_size, PROT_READ, MAP_SHARED,
                              fd, 0);
  close(fd);
}

DeviceObj::~DeviceObj() {
  sem_close(sem);
  munmap(mem, memsize);
}

void DeviceObj::UpdateFormat() {
  std::string data;
  client->GetPreviewImageInfo(data, UUID);
  nlohmann::json j = nlohmann::json::parse(data);

  std::lock_guard<std::mutex> lock(MutInf);
  width = j["Width"].get<int>();
  height = j["Height"].get<int>();
  format = Thrift::ImageFormat::type(j["Format"].get<int>());
}

BufferImage DeviceObj::Get() {
  size_t imgSize = memsize - sizeof(int64_t);
  unsigned char *newbuf = new unsigned char[imgSize];
  long frameID = ((int64_t *)mem)[0];
  sem_wait(sem);
  memcpy(newbuf, mem + sizeof(int64_t), imgSize);
  sem_post(sem);
  std::lock_guard<std::mutex> lock(MutInf);
  return BufferImage{.width = width,
                     .height = height,
                     .frameID = frameID,
                     .type = format,
                     .data = newbuf};
}
